﻿using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using NewRetail.Application.Components.Account;
using NewRetail.Application.Components.Platform;
using NewRetail.Application.Core.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;

namespace NewRetail.Application.Core
{
    public static class CoreExtension
    {
        public static void UpdateEntity<T>(this T entity, T data) where T : BaseEntity
        {
            var ignoredProps = new[] {
                nameof(IBaseEntity.Id),
                nameof(IBaseEntity.addTime),
                nameof(IBaseEntity.addUserID),
            };

            entity.TrySetFields(data, x => !ignoredProps.Contains(x.Name));
        }

        public static void TrySetFields(this object entity, object data, Func<PropertyInfo, bool> predict)
        {
            data.Should().NotBeNull();

            var props = entity.GetType().GetProperties()
                .Where(x => x.CanRead && x.CanWrite)
                .Where(x => predict.Invoke(x)).ToArray();

            var dataProps = data.GetType().GetProperties();

            foreach (var m in props)
            {
                var dataprop = dataProps.FirstOrDefault(x => x.Name == m.Name);
                if (dataprop == null || dataprop.PropertyType != m.PropertyType)
                {
                    continue;
                }

                var val = dataprop.GetValue(data);
                m.SetValue(entity, value: val);
            }
        }

        public static bool IsInWarehouse(this IInOrOutEntity entity) => entity.inOrOut == (int)InOrOutEnum.IN;

        public static string GetTypeIdentity(this Type t) => t.FullName;

        public static string NewGuidString(this IGuidGenerator guidGenerator) =>
            guidGenerator.Create().ToString().Replace("-", "");

        public static IQueryable<T> MaxTake<T>(this IQueryable<T> query) => query.Take(10000);

        public static async Task<IEnumerable<D>> AttachData<T, D>(this DbContext db,
            IEnumerable<D> source,
            Func<D, string> idGetter,
            Action<D, T[]> action)
            where T : class, IEntity<string>
        {
            if (!source.Any())
            {
                return source;
            }
            var ids = source.Select(idGetter).Distinct().ToArray();
            var alldata = await db.Set<T>().IgnoreQueryFilters().AsNoTracking().Where(x => ids.Contains(x.Id)).ToArrayAsync();

            foreach (var m in source)
            {
                var items = alldata.Where(x => idGetter(m) == x.Id).ToArray();
                action(m, items);
            }

            return source;
        }

        public static async Task<ReceiptNoItemsDto<T>> SaveReceiptItems<T>(this IRepository<T> repo, IServiceProvider serviceProvider, ReceiptNoItemsDto<T> data, string noPrefix) where T : BaseEntity, IReceiptEntity
        {
            data.Should().NotBeNull();
            data.Items.Should().NotBeNullOrEmpty();
            noPrefix.Should().NotBeNullOrEmpty();

            using var s = serviceProvider.CreateScope();

            var EntityHelper = s.ServiceProvider.GetRequiredService<IEntityHelper>();
            var noGeneratorService = s.ServiceProvider.GetRequiredService<INoGeneratorService>();
            var GuidGenerator = s.ServiceProvider.GetRequiredService<IGuidGenerator>();
            var accountService = s.ServiceProvider.GetRequiredService<IUserAccountService>();

            if (string.IsNullOrWhiteSpace(data.receiptNo))
            {
                data.receiptNo = await noGeneratorService.GenerateReceiptNoAsync(noPrefix);
            }
            else
            {
                if (await repo.AnyAsync(x => x.receiptNo == data.receiptNo && x.isAuditing))
                {
                    throw new UserFriendlyException("票据已审批，无法修改");
                }
                await repo.DeleteAsync(x => x.receiptNo == data.receiptNo, autoSave: true);
            }

            if (await repo.AnyAsync(x => x.receiptNo == data.receiptNo))
            {
                throw new UserFriendlyException("单据号已经存在");
            }

            var userModel = await accountService.GetCurrentLoginUser();
            if (userModel == null)
            {
                throw new UserFriendlyException("非法用户");
            }
            var nowTime = DateTime.Now;
            foreach (var m in data.Items)
            {
                EntityHelper.InitEntity(m);
                m.receiptNo = data.receiptNo;
                m.isAuditing = false;
                m.operateTime = nowTime;
                m.operateUserID = userModel.UID;
            }

            await repo.InsertManyAsync(data.Items, autoSave: true);

            return data;
        }
    }
}
